package Life ( sysLife, mkLife ) where

import List

-----------
-- Types --
-----------

type Coord = (Integer, Integer)
type Cell = Reg Bool

type NeighborCount = Bit 3

---------------
-- Functions --
---------------

joinRules :: List Rules -> Rules
joinRules = foldr (<+>) (rules { })

findCell :: (Eq a) => a -> List (a,b) -> b -> b
findCell i (Cons (x,y) xs) def = if (i == x)
                               then y
                               else findCell i xs def
findCell _ Nil def = def

mkRules :: List (Coord, Cell) -> (Coord, Cell) -> Rules
mkRules cs ((x, y), cell) =
    let
        isAlive :: Coord -> NeighborCount
        isAlive coord =
            let f :: (Coord, Reg Bool) -> (Coord, NeighborCount)
                f (a,b) = (a, if b then 1 else 0)
            in  findCell coord (map f cs) 1

        up = isAlive (x,y-1)
        down = isAlive (x,y+1)
        left = isAlive (x-1,y)
        right = isAlive (x+1,y)
        upleft = isAlive (x-1,y-1)
        upright = isAlive (x+1,y-1)
        dnleft = isAlive (x-1,y+1)
        dnright = isAlive (x+1,y+1)

        sum :: NeighborCount
        sum = up + down + left + right + upleft + upright + dnleft + dnright
    in
        rules
            when cell, (sum < 3) || (sum > 5)
              ==> action { cell := False }
            when not cell, (sum >= 3) && (sum <= 5)
              ==> action { cell := True }

-------------
-- Modules --
-------------

sysLife :: Module Empty
sysLife = mkLife 2 3

mkLife :: Integer -> Integer -> Module Empty
mkLife n m =
    module
        let coords :: List (Integer,Integer)
            coords = concat (map (\y -> map (\x -> (x,y)) (upto 0 n))
                                (upto 0 m))

            mkCell :: Coord -> Module (Coord, Cell)
            mkCell coord = do
                               r :: Cell
                               r <- mkReg False
                               return (coord,r)

        regs :: List (Coord, Cell)
        regs <- mapM mkCell coords
        addRules $ foldr (<+>) (rules { }) (map (mkRules regs) regs)
